#include "updater.h"
#include "fal.h"

#define LOG_TAG "app.updater"
#define LOG_LVL LOG_LVL_INFO
#include <ulog.h>

upacker_inst packer;
static char *recv_partition = DEFAULT_DOWNLOAD_PART;
static size_t update_file_total_size, update_file_cur_size;
static const struct fal_partition *dl_part = RT_NULL;
static rt_device_t serial;
rt_size_t ubuff_len; //串口缓冲区最新的长度
uint8_t over = 0;
uint8_t down_status = 0; //下载成功的状态
//下载信息
struct update_header_inst update_header;
struct update_worker_inst update_worker;

rt_err_t before_download(rt_size_t fw_size);
rt_err_t on_download(rt_size_t offset, rt_uint8_t *buf, rt_size_t len);
/**
 * @brief upacker收到数据帧后的回调函数
 * 
 * @param d 
 * @param s 
 */
void packer_handler(uint8_t *d, uint16_t s)
{
    if (s < 2)
    {
        LOG_W("error frame recived");
        return;
    }
    uint16_t cmd = *(uint16_t *)d;
    if (cmd == CMD_DOWN)
    {
        //下载固件的起始指令，后接updater_header
        //手动赋值
        uint32_t *pcmd = (uint32_t *)(d + 2);
        update_header.ota_type = *pcmd++;     // 固件的类型
        update_header.fw_size = *pcmd++;      // 要下载的固件字节长度
        update_header.fw_pack_size = *pcmd++; //要下载的固件分包的包长
        update_header.fw_pack_num = *pcmd++;  //要下载的固件分包的数量
        //
        update_worker.index_list_len = update_header.fw_pack_num / 8;
        update_worker.index_list =
            realloc(update_worker.index_list, update_worker.index_list_len);
        memset(update_worker.index_list, 0, update_worker.index_list_len);
        LOG_I("GET Firmware updater header, fw size:%d, pack size:%d, pack number:%d", update_header.fw_size, update_header.fw_pack_size, update_header.fw_pack_num);

        //按位计数，最后不足8位的提前补1；
        uint8_t diff_len = update_header.fw_pack_num % 8;
        for (int i = diff_len; i < 8; i++)
        {
            update_worker.index_list[update_worker.index_list_len - 1] |= 1 << i;
        }

        if (dl_part == RT_NULL || before_download(update_header.fw_size) < 0)
        {
            LOG_E("Firmware download failed! Partition (%s) find error!", recv_partition);
            return;
        }
        else
        {
            //回复，开始传输数据包
            uint16_t reply = CMD_DOWN_REPLY;
            upacker_pack(&packer, &reply, sizeof(reply));
        }
    }
    else if (cmd == CMD_CTL)
    {
        //控制指令
    }
    else if (cmd == CMD_PACK)
    {
        //2字节ID+固定长度数据
        //下载固件数据包传输
        uint16_t index = (*(uint16_t *)(d + 2));
        uint32_t offset = index * update_header.fw_pack_size; //当前包id，从0开始
        uint8_t *pdata = d + 4;
        if (s != (4 + update_header.fw_pack_size))
        {
            LOG_W("error fw pack size:%d, should be:%d!", s - 4, update_header.fw_pack_size);
            return;
        }
        //回复，开始准备传输下一个数据包
        uint16_t reply = CMD_PACK_REPLY;
        upacker_pack(&packer, &reply, sizeof(reply));
        LOG_I("GET Firmware pack index:%d", index);

        //写入flash
        on_download(offset, pdata, update_header.fw_pack_size);
    }
}
void packer_usend(uint8_t *d, uint16_t s)
{
    rt_device_write(serial, 0, d, s);
}

/**
 * @brief 初始化
 * 
 */
rt_err_t init_updater()
{
    char *file_name, *file_size;

    //初始化packer
    upacker_init(&packer, packer_handler, packer_usend);

    /* Get download partition information and erase download partition data */
    if ((dl_part = fal_partition_find(recv_partition)) == RT_NULL)
    {
        LOG_E("Firmware download failed! Partition (%s) find error!", recv_partition);
        return -1;
    }
    return 0;
}

rt_err_t before_download(rt_size_t fw_size)
{

    rt_kprintf("update file_size:%d\n", fw_size);

    update_file_cur_size = 0;

    if (fw_size > dl_part->len)
    {
        LOG_E("Firmware is too large! File size (%d), '%s' partition size (%d)", fw_size, recv_partition, dl_part->len);
        return -1;
    }

    LOG_I("Start erase. Size (%d)", fw_size);

    /* erase DL section */
    if (fal_partition_erase(dl_part, 0, fw_size) < 0)
    {
        LOG_E("Firmware download failed! Partition (%s) erase error!", dl_part->name);
        return -2;
    }

    return 0;
}

rt_err_t on_download(rt_size_t offset, rt_uint8_t *buf, rt_size_t len)
{
    /* write data of application to DL partition  */
    if (fal_partition_write(dl_part, offset, buf, len) < 0)
    {
        LOG_E("Firmware download failed! Partition (%s) write data error!", dl_part->name);
        return -1;
    }
    return 0;
}

/* 用于接收消息的信号量 */
static struct rt_semaphore rx_sem;

/* 接收数据回调函数 */
static rt_err_t uart_input(rt_device_t dev, rt_size_t size)
{
    /* 串口接收到数据后产生中断，调用此回调函数，然后发送接收信号量 */
    rt_sem_release(&rx_sem);

    return RT_EOK;
}

static int update_sample(int argc, char *argv[])
{
    rt_err_t ret = RT_EOK;

    /* 查找系统中的串口设备 */
    serial = rt_console_get_device();
    if (!serial)
    {
        rt_kprintf("find serial device failed!\n");
        return RT_ERROR;
    }

    /* 初始化信号量 */
    rt_sem_init(&rx_sem, "prx_sem", 0, RT_IPC_FLAG_FIFO);
    /* 以中断接收及轮询发送模式打开串口设备 */
    rt_device_open(serial, RT_DEVICE_FLAG_INT_RX);
    /* 设置接收回调函数 */
    rt_device_set_rx_indicate(serial, uart_input);
    char ch;
    rt_uint32_t rx_length;
    while (!over)
    {
        /* 从串口读取一个字节的数据，没有读取到则等待接收信号量 */
        while (rt_device_read(serial, -1, &ch, 1) < 1)
        {
            /* 阻塞等待接收信号量，等到信号量后再次读取数据 */
            rt_sem_take(&rx_sem, RT_WAITING_FOREVER);
        }
        upacker_unpack(&packer, ch, 1);
    }

    return ret;
}

void uupdater(uint8_t argc, char **argv)
{

    if (argc < 2)
    {
        recv_partition = DEFAULT_DOWNLOAD_PART;
        rt_kprintf("Default save firmware on download partition.\n");
    }
    else
    {
        const char *operator= argv[1];
        if (!strcmp(operator, "-p"))
        {
            if (argc < 3)
            {
                rt_kprintf("Usage: uupdater -p <partiton name>.\n");
                return;
            }
            else
            {
                /* change default partition to save firmware */
                recv_partition = argv[2];
            }
        }
        else
        {
            rt_kprintf("Usage: uupdater -p <partiton name>.\n");
            return;
        }
    }

    rt_kprintf("Warning: uupdater has started! This operator will not recovery.\n");
    rt_kprintf("Please select the ota firmware file and use uupdater to send.\n");

    init_updater();

    if (!update_sample(0, 0))
    {
        rt_kprintf("Download firmware to flash success.\n");

        rt_kprintf("System now will restart...\r\n");

        /* wait some time for terminal response finish */
        rt_thread_delay(rt_tick_from_millisecond(200));

        /* Reset the device, Start new firmware */
        extern void rt_hw_cpu_reset(void);
        rt_hw_cpu_reset();
        /* wait some time for terminal response finish */
        rt_thread_delay(rt_tick_from_millisecond(200));
    }
    else
    {
        /* wait some time for terminal response finish */
        rt_thread_delay(RT_TICK_PER_SECOND);
        rt_kprintf("Update firmware fail.\n");
    }

    return;
}

MSH_CMD_EXPORT(uupdater, Use uupdater to download the firmware);
